-------------------------------------------------------------------- tests cases

type FunctionPieces a b = (a -> Bool, a -> b, a -> [b] -> b, [a -> a])

-------------------------------------------------------------------- tests cases

funPieces :: FunctionPieces a b -> a -> b
funPieces setOfPieces = f
  where f x = if isBase x then triv x else combine x [f (t x) | t <- ts]
        (isBase, triv, combine, ts) = setOfPieces

factorial :: Int -> Int             ----------------------------------- Factorial
factorial = funPieces factorialPieces

factorialPieces :: FunctionPieces Int Int
factorialPieces = (isBase, triv, combine, ts)
  where 
    isBase n = n==0
    triv   n = 1
    combine n [m] = n*m
    ts       = [\n->n-1]

fibonacci :: Int -> Int            ----------------------------------- Fibonacci
fibonacci = funPieces fibonacciPieces

fibonacciPieces :: FunctionPieces Int Int
fibonacciPieces = (isBase, triv, combine, ts)
  where 
    isBase n = n==0 || n==1
    triv   n = 1
    combine _ [m1, m2] = m1 + m2
    ts       = [\n->n-1, \n->n-2]

addNumbers :: [Int] -> Int        ----------------------------------- addNumbers
addNumbers = funPieces addNumbersPieces

addNumbersPieces :: FunctionPieces [Int] Int
addNumbersPieces = (isBase, triv, combine, ts)
  where 
    isBase l = l==[]
    triv   l = 0
    combine (x:_) [tailSum] = x + tailSum
    ts       = [\(_:xx) -> xx]

tresNmas1 :: Int -> Int           ------------------------------------ tresNmas1
tresNmas1 = funPieces tresNmas1Pieces

tresNmas1Pieces :: FunctionPieces Int Int
tresNmas1Pieces = (isBase, triv, combine, ts)
  where 
    isBase n = n==1
    triv   n = 0
    combine _ [numPasos] = numPasos + 1
    ts       = [\n->if even n then div n 2 else 3*n+1]

isSortedTree :: Ord a => BinTree a -> Bool ------------------------ isSortedTree
isSortedTree = funPieces isSortedTreePieces

data BinTree a = Empty | BinNode a (BinTree a) (BinTree a) deriving Eq

isSortedTreePieces :: Ord a => FunctionPieces (BinTree a) Bool
isSortedTreePieces = (isBase, triv, combine, ts)
  where
    isBase t = t == Empty
    triv   t = True
    combine (BinNode a t1 t2) [isSortedT1, isSortedT2] = 
      case t1 of 
        Empty         -> True
        BinNode _ _ _ -> isSortedT1 && a > maxElem t1
      &&
      case t2 of 
        Empty         -> True
        BinNode _ _ _ -> isSortedT2 && a < minElem t2
    ts       = [\(BinNode _ t1 _) -> t1, \(BinNode _ _ t2) -> t2]

minElem :: Ord a => BinTree a -> a
minElem (BinNode a Empty              _) = a
minElem (BinNode _ t1@(BinNode _ _ _) _) = minElem t1
maxElem :: Ord a => BinTree a -> a
maxElem (BinNode a _ Empty             ) = a
maxElem (BinNode _ _ t2@(BinNode _ _ _)) = maxElem t2

quickSort :: Ord a => [a] -> [a] ------------------------------------- quickSort
quickSort = funPieces quickSortPieces

quickSortPieces :: Ord a => FunctionPieces [a] [a]
quickSortPieces = (isBase, triv, combine, ts)
  where 
    isBase l = l==[]
    triv   l = []
    combine xx [xx1, xx2] = xx1 ++ [head xx] ++ xx2
    ts       = [\xx -> filter (< head xx) (tail xx), 
                \xx -> filter (>= head xx) (tail xx)]

fibonacciLog :: Int -> Int  --------------------------------------- fibonacciLog
fibonacciLog = fst . fib2
 
fib2 :: Int -> (Int, Int)
fib2 = funPieces fib2Pieces

fib2Pieces = (isBase, triv, combine, ts)
  where 
    isBase n = (n==0) || (n==1)
    triv   0 = (1, 1)
    triv   1 = (1, 2)
    combine n [fib2']
      | even n    = (a*a + b*b, c*c - a*a)
      | otherwise = (c*c - a*a, b*b + c*c)
      where (a,b) = fib2'
            c     = a + b
    ts = [\n -> n `div` 2 - 1]

